﻿using JX.Infrastructure.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;
using System.Text;

namespace JX.Infrastructure.Attribute
{
	/// <summary>
	/// Attribute帮助类
	/// </summary>
	public class AttrHelper
	{
        #region MyFlag特性的值/存在特定的特性值
        /// <summary>
        /// 得到指定字段名的自定义特性的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fieldName">要查找特性值的字段名</param>
        /// <returns></returns>
        public static string GetMyFlagValue<T>(string fieldName) where T : class
		{
            if (string.IsNullOrWhiteSpace(fieldName))
            {
                return string.Empty;
            }
            //通过活化剂类来实例化泛型类
            T t = Activator.CreateInstance<T>();
            PropertyInfo[] properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (properties.Length <= 0)
            {
                return string.Empty;
            }
            string result = string.Empty;
            foreach (PropertyInfo item in properties)
            {
                if (fieldName != item.Name) continue;
                if (item.IsDefined(typeof(MyFlagAttribute)))
                {
                    foreach (System.Attribute attr in item.GetCustomAttributes(typeof(MyFlagAttribute), false))
                    {
                        var myFlag = attr as MyFlagAttribute;
                        if (myFlag != null)
                        {
                            result = myFlag.Flag;
                            break;
                        }
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 得到自定义特性值的集合。集合中的内容为《字段名，特性值》
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static Dictionary<string, string> GetMyFlagValue<T>() where T : class
        {
            //通过活化剂类来实例化泛型类
            T t = Activator.CreateInstance<T>();
            PropertyInfo[] properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (properties.Length <= 0)
            {
                return null;
            }
            Dictionary<string, string> dict = new Dictionary<string, string>();
            foreach (PropertyInfo item in properties)
            {
                if (item.IsDefined(typeof(MyFlagAttribute)))
                {
                    foreach (System.Attribute attr in item.GetCustomAttributes(typeof(MyFlagAttribute), false))
                    {
                        var myFlag = attr as MyFlagAttribute;
                        if (myFlag != null)
                        {
                            dict.Add(item.Name, myFlag.Flag);
                        }
                    }
                }
            }
            return dict;
        }

        /// <summary>
        /// 指定字段名上是否存在特定的特性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fieldName">实体类的属性名</param>
        /// <param name="attrValue">特性值</param>
        /// <returns></returns>
        public static bool IsExistMyFlagValue<T>(string fieldName, string attrValue) where T : class
        {
            if(string.IsNullOrEmpty(fieldName) || string.IsNullOrEmpty(attrValue))
            {
                return false;
            }
            var result = GetMyFlagValue<T>(fieldName);
            return StringHelper.FoundCharInArr(attrValue, result, "|");
        }
        #endregion

        #region 主键的名称/是否主键
        /// <summary>
        /// 得到第一个标记了主键的名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static string GetKeyName<T>() where T : class
        {
            //通过活化剂类来实例化泛型类
            T t = Activator.CreateInstance<T>();
            PropertyInfo pkProp = t.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length > 0).FirstOrDefault();
            //主键名称
            return (pkProp == null) ? string.Empty : pkProp.Name;
        }

        /// <summary>
        /// 得到所有标记了主键的名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static List<string> GetKeyNameList<T>() where T : class
        {
            //通过活化剂类来实例化泛型类
            T t = Activator.CreateInstance<T>();
            var properties = t.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length > 0);
            if (properties.Count() <= 0)
            {
                return null;
            }
            List<string> dict = new List<string>();
            foreach (PropertyInfo item in properties)
            {
                dict.Add(item.Name);
            }
            return dict;
        }

        /// <summary>
        /// 指定的字段名是否主键
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fieldName">实体类的属性名</param>
        /// <returns></returns>
        public static bool IsPrimaryKey<T>(string fieldName) where T : class
        {
            var list = GetKeyNameList<T>();
            return list.Contains(fieldName);
        }
        #endregion

        #region 实体类的属性名和值
        /// <summary>
        /// 得到泛型类的所有属性名列表。主键列通过[Key]标记；自增列通过[MyFlag("Identity")]标记；
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="excludeType">排除类型：0:不排除；1:排除自动增长列；2:排除主键列；3:排除自动增长列或主键列；4:排除自动增长列和主键列；</param>
        /// <param name="ignoreNotMapped">是否忽略NotMapped特性的列</param>
        /// <returns></returns>
        public static List<string> GetPropertiesName<T>(int excludeType=0,bool ignoreNotMapped=true) where T : class
        {
            //通过活化剂类来实例化泛型类
            T t = Activator.CreateInstance<T>();
            var properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            if (properties.Count() <= 0)
            {
                return null;
            }
            List<string> dict = new List<string>();
            foreach (PropertyInfo item in properties)
            {
                bool isNotMapped = item.IsDefined(typeof(NotMappedAttribute));
                if(ignoreNotMapped && isNotMapped)
                {
                    continue;
                }
                bool isPK = item.IsDefined(typeof(KeyAttribute));
                bool isIdentity = false;
                if (item.IsDefined(typeof(MyFlagAttribute)))
                {
                    foreach (System.Attribute attr in item.GetCustomAttributes(typeof(MyFlagAttribute), false))
                    {
                        var myFlag = attr as MyFlagAttribute;
                        if (myFlag != null)
                        {
                            isIdentity = StringHelper.FoundCharInArr("Identity", myFlag.Flag, "|");
                        }
                    }
                }
                switch (excludeType)
                {
                    case 1://排除自动增长列
                        if (isIdentity) 
                            break;
                        else 
                            dict.Add(item.Name);
                        break;
                    case 2://排除主键列
                        if (isPK) 
                            break;
                        else 
                            dict.Add(item.Name);
                        break;
                    case 3://排除自动增长列或主键列
                        if (isIdentity) 
                            break;
                        else if(isPK) 
                            break;
                        else 
                            dict.Add(item.Name);
                        break;
                    case 4://排除自动增长列和主键列
                        if (isIdentity && isPK)
                            break;
                        else
                            dict.Add(item.Name);
                        break;
                    default:
                        dict.Add(item.Name);
                        break;
                }
            }
            return dict;
        }

        /// <summary>
        /// 得到实体类的字段名称和值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static Dictionary<string, string> GetPropertiesNameValue<T>(T t) where T : class
        {
            if (t == null)
            {
                return null;
            }
            var properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            if (properties.Length <= 0)
            {
                return null;
            }
            Dictionary<string, string> ret = new Dictionary<string, string>();
            foreach (PropertyInfo item in properties)
            {
                string name = item.Name;//实体类字段名称
                string value = DataConverter.ToString(item.GetValue(t, null));//该字段的值

                if (item.PropertyType.IsValueType || item.PropertyType.Name.StartsWith("String"))
                {
                    ret.Add(name, value);//在此可转换value的类型
                }
            }
            return ret;
        }
        /// <summary>
        /// 为实体类的字段名和值进行赋值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <param name="d"></param>
        /// <returns></returns>
        public static T SetPropertiesNameValue<T>(T t, Dictionary<string, string> d) where T : class
        {
            if (t == null || d == null)
            {
                return default(T);
            }
            var properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            if (properties.Length <= 0)
            {
                return default(T);
            }
            foreach (PropertyInfo item in properties)
            {
                string name = item.Name;
                string value = DataConverter.ToString(item.GetValue(t, null));

                if (item.PropertyType.IsValueType || item.PropertyType.Name.StartsWith("String"))
                {
                    string val = d.Where(c => c.Key == name).FirstOrDefault().Value;
                    if (val != null && val != value)
                    {
                        Type type = item.PropertyType;
                        if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                        {
                            NullableConverter nullableConverter = new NullableConverter(type);
                            type = nullableConverter.UnderlyingType;
                        }
                        object objValue = Convert.ChangeType(val, type);
                        item.SetValue(t, objValue, null);
                    }
                }
            }
            return t;
        }
        #endregion
    }
}
